using Microsoft.Extensions.Options;
using Qiniu.Http;
using Qiniu.Storage;
using Qiniu.Util;
using Traceability.API.Config;
using Traceability.API.Models;

namespace Traceability.API.Services
{
    /// <summary>
    /// 七牛云服务实现
    /// </summary>
    public class QiNiuService : IQiNiuService
    {
        private readonly QiNiuOptions _options;
        private readonly ILogger<QiNiuService> _logger;
        private readonly Mac _mac;
        private readonly Qiniu.Storage.Config _config;

        public QiNiuService(IOptions<QiNiuOptions> options, ILogger<QiNiuService> logger)
        {
            _options = options.Value;
            _logger = logger;
            _mac = new Mac(_options.AccessKey, _options.SecretKey);
            
            // 配置七牛云
            _config = new Qiniu.Storage.Config();
            _config.Zone = GetZone(_options.Zone);
            _config.UseHttps = _options.UseHttps;
            _config.UseCdnDomains = _options.UseCdnDomains;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        public async Task<QiNiuUploadResponse> UploadFileAsync(IFormFile file, string? key = null)
        {
            try
            {
                // 验证文件
                var (isValid, message) = ValidateFile(file);
                if (!isValid)
                {
                    return new QiNiuUploadResponse
                    {
                        Success = false,
                        Message = message
                    };
                }

                // 生成文件Key
                if (string.IsNullOrEmpty(key))
                {
                    key = GenerateFileKey(file.FileName);
                }

                // 转换为流并上传
                using var stream = file.OpenReadStream();
                return await UploadStreamAsync(stream, file.FileName, key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "上传文件失败: {FileName}", file.FileName);
                return new QiNiuUploadResponse
                {
                    Success = false,
                    Message = $"上传失败: {ex.Message}"
                };
            }
        }

        /// <summary>
        /// 上传文件流
        /// </summary>
        public async Task<QiNiuUploadResponse> UploadStreamAsync(Stream stream, string fileName, string? key = null)
        {
            try
            {
                // 生成文件Key
                if (string.IsNullOrEmpty(key))
                {
                    key = GenerateFileKey(fileName);
                }

                // 获取上传Token
                var token = GetUploadToken(key).Token;

                // 创建上传管理器
                var uploadManager = new UploadManager(_config);

                // 执行上传
                var result = await Task.Run(() => uploadManager.UploadStream(stream, key, token, null));

                if (result.Code == 200)
                {
                    return new QiNiuUploadResponse
                    {
                        Success = true,
                        Message = "上传成功",
                        Url = GetFileUrl(key),
                        Key = key,
                        Size = stream.Length,
                        ContentType = GetContentType(fileName),
                        UploadTime = DateTime.Now
                    };
                }
                else
                {
                    _logger.LogError("七牛云上传失败: {Code} - {Text}", result.Code, result.Text);
                    return new QiNiuUploadResponse
                    {
                        Success = false,
                        Message = $"上传失败: {result.Text}"
                    };
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "上传文件流失败: {FileName}", fileName);
                return new QiNiuUploadResponse
                {
                    Success = false,
                    Message = $"上传失败: {ex.Message}"
                };
            }
        }

        /// <summary>
        /// 删除文件
        /// </summary>
        public async Task<QiNiuDeleteResponse> DeleteFileAsync(string key)
        {
            try
            {
                var bucketManager = new BucketManager(_mac, _config);
                var result = await Task.Run(() => bucketManager.Delete(_options.Bucket, key));

                if (result.Code == 200)
                {
                    return new QiNiuDeleteResponse
                    {
                        Success = true,
                        Message = "删除成功",
                        Key = key
                    };
                }
                else
                {
                    _logger.LogError("七牛云删除文件失败: {Code} - {Text}", result.Code, result.Text);
                    return new QiNiuDeleteResponse
                    {
                        Success = false,
                        Message = $"删除失败: {result.Text}",
                        Key = key
                    };
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "删除文件失败: {Key}", key);
                return new QiNiuDeleteResponse
                {
                    Success = false,
                    Message = $"删除失败: {ex.Message}",
                    Key = key
                };
            }
        }

        /// <summary>
        /// 获取上传Token
        /// </summary>
        public QiNiuTokenResponse GetUploadToken(string? key = null)
        {
            try
            {
                var putPolicy = new PutPolicy();
                putPolicy.Scope = string.IsNullOrEmpty(key) ? _options.Bucket : $"{_options.Bucket}:{key}";
                putPolicy.SetExpires(_options.TokenExpires);

                // 设置自动删除
                if (_options.DeleteAfterDays.HasValue)
                {
                    putPolicy.DeleteAfterDays = _options.DeleteAfterDays.Value;
                }

                var token = Auth.CreateUploadToken(_mac, putPolicy.ToJsonString());
                var expireTime = DateTime.Now.AddSeconds(_options.TokenExpires);

                return new QiNiuTokenResponse
                {
                    Token = token,
                    ExpireTime = expireTime,
                    UploadUrl = GetUploadUrl()
                };
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成上传Token失败");
                throw;
            }
        }

        /// <summary>
        /// 获取文件访问URL
        /// </summary>
        public string GetFileUrl(string key)
        {
            var baseUrl = _options.BaseUrl.TrimEnd('/');
            return $"{baseUrl}/{key}";
        }

        /// <summary>
        /// 验证文件
        /// </summary>
        public (bool IsValid, string Message) ValidateFile(IFormFile file)
        {
            if (file == null || file.Length == 0)
            {
                return (false, "文件不能为空");
            }

            // 检查文件大小
            var maxSizeBytes = _options.MaxFileSizeMB * 1024 * 1024;
            if (file.Length > maxSizeBytes)
            {
                return (false, $"文件大小不能超过 {_options.MaxFileSizeMB}MB");
            }

            // 检查文件类型
            var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
            if (_options.AllowedFileTypes.Any() && !_options.AllowedFileTypes.Contains(extension))
            {
                return (false, $"不支持的文件类型: {extension}");
            }

            return (true, "验证通过");
        }

        /// <summary>
        /// 生成文件Key
        /// </summary>
        public string GenerateFileKey(string fileName)
        {
            var extension = Path.GetExtension(fileName);
            var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
            var guid = Guid.NewGuid().ToString("N")[..8];
            return $"uploads/{DateTime.Now:yyyy/MM/dd}/{timestamp}_{guid}{extension}";
        }

        #region 私有方法

        /// <summary>
        /// 获取存储区域
        /// </summary>
        private Qiniu.Storage.Zone GetZone(string zoneName)
        {
            return zoneName.ToUpper() switch
            {
                "CN_EAST" => Qiniu.Storage.Zone.ZONE_CN_East,
                "CN_NORTH" => Qiniu.Storage.Zone.ZONE_CN_North,
                "CN_SOUTH" => Qiniu.Storage.Zone.ZONE_CN_South,
                "US_NORTH" => Qiniu.Storage.Zone.ZONE_US_North,
                "AS_SINGAPORE" => Qiniu.Storage.Zone.ZONE_AS_Singapore,
                _ => Qiniu.Storage.Zone.ZONE_CN_East
            };
        }

        /// <summary>
        /// 获取上传域名
        /// </summary>
        private string GetUploadUrl()
        {
            // 根据区域返回上传域名
            return _options.Zone.ToUpper() switch
            {
                "CN_EAST" => _options.UseHttps ? "https://up.qiniup.com" : "http://up.qiniup.com",
                "CN_NORTH" => _options.UseHttps ? "https://up-z1.qiniup.com" : "http://up-z1.qiniup.com",
                "CN_SOUTH" => _options.UseHttps ? "https://up-z2.qiniup.com" : "http://up-z2.qiniup.com",
                "US_NORTH" => _options.UseHttps ? "https://up-na0.qiniup.com" : "http://up-na0.qiniup.com",
                "AS_SINGAPORE" => _options.UseHttps ? "https://up-as0.qiniup.com" : "http://up-as0.qiniup.com",
                _ => _options.UseHttps ? "https://up.qiniup.com" : "http://up.qiniup.com"
            };
        }

        /// <summary>
        /// 获取文件MIME类型
        /// </summary>
        private string GetContentType(string fileName)
        {
            var extension = Path.GetExtension(fileName).ToLowerInvariant();
            return extension switch
            {
                ".jpg" or ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".gif" => "image/gif",
                ".bmp" => "image/bmp",
                ".webp" => "image/webp",
                ".pdf" => "application/pdf",
                ".doc" => "application/msword",
                ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ".xls" => "application/vnd.ms-excel",
                ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                ".ppt" => "application/vnd.ms-powerpoint",
                ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                _ => "application/octet-stream"
            };
        }

        #endregion
    }
}
